function deepMerge (target = {}, source = {}) {
    for(let key in source) {
        if(source.hasOwnProperty(key)) {
            if(typeof source[key] === 'object' && source[key] !== null) {
                // 如果属性值是对象，则递归进行深合并
                if(target[key] === undefined) {
                    // 如果目标对象中没有该属性，则直接合并
                    target[key] = {}
                }
                deepMerge(target[key], source[key])
            } else {
                // 如果属性值不是对象，则直接赋值
                target[key] = source[key]
            }
        }
    }  
    return target 
}
const baseChartStyle = {
    strokeStyle: 'blue',
    lineWidth: 5,
    fillStyle: 'blue',
}
const baseOptions = {
    padding: [20,20,20,20]
}
const baseCoordConfig = {
    xAxis: {
        type: 'category',
        valueName: 'time',
        paddingTop: 10,
        paddingBottom: 0
    },
    yAxis: {
        type: 'value',
        valueName: 'value',
        paddingTop: 10,
        paddingBottom: 0
    }
}
class MyChart {
    constructor(options) {
        // 设置dom元素
        this.dom = options.container
        // 将dom元素转化为canvas对象
        this.canvasCtx = this.dom.getContext("2d")
        this.padding = options.padding || baseOptions.padding
        // 设置容器的宽高，兼容2倍屏 window.devicePixelRatio
        this.pixelRatio = window.devicePixelRatio  // 获取设备像素比
        options.width ? this.dom.width = options.width * this.pixelRatio : ''
        options.height ? this.dom.height = options.height * this.pixelRatio: ''
        this.dom.style = `width:${this.dom.width / this.pixelRatio}px; height: ${this.dom.height / this.pixelRatio}px`
    }
    // 渲染函数
    render(opt = {}) {
        this.opt = opt
        this.opt.style = deepMerge(baseChartStyle, opt.style)
        this.coord = deepMerge(baseCoordConfig, opt.coord)
        this.setCoordInfo()
        this.renderCoordinate()
        switch(this.opt.type) {
            case 'line':
                this.renderLine()
                break
            case 'bar':
                this.renderBar()
                break
        }
    }
    // 设置坐标系的信息
    setCoordInfo() {
        const sourceData = this.opt.data
        const axisArr = [this.coord.xAxis, this.coord.yAxis]
        axisArr.forEach(axisObj => {
            let axisType = axisObj.type
            const axisValueName = axisObj.valueName
            sourceData.forEach(dataItem => {
                const numberValue = Number(dataItem[axisValueName])
                if(!isNaN(numberValue) && typeof numberValue === 'number') {
                    axisType = 'value'
                } else {
                    axisType = 'category'
                }
            })
            if(axisType === 'value') {
                axisObj.min = 0
                let _sourceData = sourceData.slice()
                let _sortArr = _sourceData.sort((a, b) => b[axisValueName] - a [axisValueName])
                axisObj.max = _sortArr[0][axisValueName]
                // 默认刻度个数
                let intervalNum = 5
                let interval = axisObj.max / intervalNum // 刻度间距
                let labelData = []
                for(let i = 0; i<= intervalNum+1; i++) {
                    labelData.push(Math.floor(i * interval))
                }
                axisObj.labelData = labelData
            } else if(axisType === 'category') {
                axisObj.labelData = sourceData.map(item => item[axisValueName])
                
            }
        })
        this.coord.coordSize = {
            height: this.dom.height - this.padding[0] - this.padding[2],
            width: this.dom.width - this.padding[1] - this.padding[3]
        }
    }
    // 渲染坐标系
    renderCoordinate() {
        let _ctx = this.canvasCtx
        let opt = this.opt
        const { xAxis, yAxis, coordSize } = this.coord
        function renderAxisLine() {
            // 确认原点
            const origin = this.transformToHtmlCoord({
                x: 0, 
                y: 0
            })
            // 画 x 轴
            _ctx.beginPath()
            _ctx.moveTo(origin.x, origin.y)
            _ctx.lineTo(origin.x + coordSize.width, origin.y)
            // _ctx.strokeStyle = opt.style.strokeStyle
            // _ctx.lineWidth = opt.style.lineWidth
            _ctx.stroke()

            // 画 x 轴
            _ctx.beginPath()
            _ctx.moveTo(origin.x, origin.y)
            _ctx.lineTo(origin.x, origin.y - coordSize.height)
            // _ctx.strokeStyle = opt.style.strokeStyle
            // _ctx.lineWidth = opt.style.lineWidth
            _ctx.stroke()
        }
        function renderAxisText() {
            xAxis.labelData.forEach(text => {
                const position = this.transformToHtmlCoord({
                    x: text,
                    y: 0
                })
                _ctx.font = "24px serif";
                _ctx.textAlign = 'center'
                _ctx.fillText(text, position.x, position.y + 30)
            })
            yAxis.labelData.forEach(text => {
                const position = this.transformToHtmlCoord({
                    x: 0,
                    y: text
                })
                _ctx.font = "24px serif";
                _ctx.textAlign = 'center'
                _ctx.fillText(text, position.x-30, position.y)
            })
        }
        renderAxisLine.call(this)
        renderAxisText.call(this)
    }
    // 将数据里面的点转化成 html 的坐标
    transformToHtmlCoord(point) {
        const { xAxis, yAxis, coordSize } = this.coord
        const padding = this.padding
        const dom = this.dom
        const xUseLength = coordSize.width - xAxis.paddingTop - xAxis.paddingBottom
        const yUseLength = coordSize.height - yAxis.paddingTop - yAxis.paddingBottom
        let htmlX = 0, htmlY = 0
        const handleObj = {
            value(xOrY, value) {
                if(xOrY === 'x') {
                    const percent = value / xAxis.labelData[xAxis.labelData.length-1]
                    const len = percent * xUseLength
                    return len + padding[3] + xAxis.paddingBottom
                }
                if(xOrY === 'y') {
                    const percent = value / yAxis.labelData[yAxis.labelData.length-1]
                    const len = percent * yUseLength
                    return coordSize.height - len + padding[2] - yAxis.paddingBottom
                }
            },
            category(xOrY, value) {
                if(xOrY === 'x') {
                    let index = xAxis.labelData.indexOf(value)
                    const percent = index / (xAxis.labelData.length - 1)
                    let len = percent * xUseLength
                    return len + padding[3] + xAxis.paddingBottom
                }
                if(xOrY === 'y') {
                    let index = yAxis.labelData.indexOf(value)
                    const percent = index / (yAxis.labelData.length - 1)
                    let len = percent * yUseLength
                    return coordSize.height - len + padding[2] - yAxis.paddingBottom
                }
            }
        }
        if(point.x === 0) {
            htmlX=padding[3]
        } else {
            htmlX = handleObj[xAxis.type]('x', point.x)
        }
        if(point.y === 0){
            htmlY=dom.height - padding[2]
        } else {
            htmlY = handleObj[yAxis.type]('y', point.y)
        }
        return {
            x: htmlX,
            y: htmlY
        }
    }
    renderBar() {
        const sourceData = this.opt.data
        const _ctx = this.canvasCtx
        const { xAxis, yAxis } = this.coord
        const padding = this.padding
        _ctx.beginPath()
        sourceData.forEach((item, index) => {
            const position = this.transformToHtmlCoord({
                x: item[xAxis.valueName],
                y: item[yAxis.valueName]
            })
            _ctx.fillStyle = this.opt.style.fillStyle
            _ctx.fillRect(position.x, position.y, 20, this.dom.height - position.y - padding[2])
        })
    }
    renderLine() {
        const sourceData = this.opt.data
        const _ctx = this.canvasCtx
        const { xAxis, yAxis } = this.coord
        _ctx.beginPath()
        sourceData.forEach((item, index) => {
            const position = this.transformToHtmlCoord({
                x: item[xAxis.valueName],
                y: item[yAxis.valueName]
            })
            if(index === 0){
                _ctx.moveTo(position.x, position.y)
            } else {
                _ctx.lineTo(position.x, position.y)
            }
            _ctx.strokeStyle = this.opt.style.strokeStyle
            _ctx.lineWidth = this.opt.style.lineWidth
            _ctx.stroke()
        })
    }
}

export default MyChart